#include "msp_flash.h"
// 
#include <stdio.h>
// 
#include "stm32f1xx_ll_utils.h"
#include "stm32f1xx_ll_pwr.h"
// #include "stm32f1xx_ll_flash.h"
// 
#include "debug.h"

// LL_PWR_IsActiveFlag_FLASH_RDY
// LL_GetPackageType();
// LL_GetFlashSize();

/**
 * @brief 内部Flash初始化
 * 
 * @return uint8_t 
 */
uint8_t Msp_Flash_Init(void)
{
    // // 初始化Flash操作
    // LL_FLASH_SetLatency(LL_FLASH_LATENCY_1);
    // if (LL_FLASH_IsActiveFlag_EOP() != SUCCESS) {
    //     // 错误处理
    // }
    // LL_FLASH_EnablePrefetch();
    // LL_FLASH_DisableRunPowerDown();

    // if (!LL_PWR_IsActiveFlag_FLASH_RDY())                                           // 检查是否初始化完毕
    // {
    //     _DEBUG_PRINT(DBG_ERR, "Error: FLASH has not been initialized......\r\n");
    //     return ERROR;
    // }
    // if (Flash_SIZE != (LL_GetFlashSize() * 1024))                                   // 确认Flash实际大小与宏定义是否一致
    // {
    //     _DEBUG_PRINTF(DBG_ERR, "Error: Flash size unmatched. Theoretical val=[%d]KB, actual val=[%d]KB\r\n", 
    //                             Flash_SIZE/1024, LL_GetFlashSize());
    //     return ERROR;
    // }
    return SUCCESS;
}

/**
 * @brief 获取某个地址所在的flash扇区
 * 
 * @param addr 待判断的flash地址
 * @return uint8_t addr所在的扇区
 */
uint8_t Msp_Flash_Get_Sector(uint32_t addr)
{
    #if USE_STM32G0x0_FLASH_SET
    if ((addr < STM32_FLASH_BASE) || (addr > STM32_FLASH_END_))
        return 0xFF;
    return (addr-STM32_FLASH_BASE)/FLASH_SECTOR_SIZE;
    #endif
    #if USE_STM32F1xx_FLASH_SET
    if ((addr < STM32_FLASH_BASE) || (addr > STM32_FLASH_END_))
        return 0xFF;
    return (addr-STM32_FLASH_BASE)/FLASH_SECTOR_SIZE;
    #endif
    #if USE_STM32F40x_FLASH_SET
    if (addr < ADDR_FLASH_SECTOR_1)
        return FLASH_SECTOR_0;
    else if (addr < ADDR_FLASH_SECTOR_2)
        return FLASH_SECTOR_1;
    else if (addr < ADDR_FLASH_SECTOR_3)
        return FLASH_SECTOR_2;
    else if (addr < ADDR_FLASH_SECTOR_4)
        return FLASH_SECTOR_3;
    else if (addr < ADDR_FLASH_SECTOR_5)
        return FLASH_SECTOR_4;
    else if (addr < ADDR_FLASH_SECTOR_6)
        return FLASH_SECTOR_5;
    else if (addr < ADDR_FLASH_SECTOR_7)
        return FLASH_SECTOR_6;
    else if (addr < ADDR_FLASH_SECTOR_8)
        return FLASH_SECTOR_7;
    else if (addr < ADDR_FLASH_SECTOR_9)
        return FLASH_SECTOR_8;
    else if (addr < ADDR_FLASH_SECTOR_10)
        return FLASH_SECTOR_9;
    else if (addr < ADDR_FLASH_SECTOR_11)
        return FLASH_SECTOR_10;
    else if (addr < ADDR_FLASH_SECTOR_12)
        return FLASH_SECTOR_11;
    return 0;
    #endif
}

/**
 * @brief 直接擦除包含这个地址的整个页（扇区）
 * 
 * @param addrx 这个扇区的任何一个地址
 * @return uint8_t 0-fail
 */
uint8_t Msp_Flash_Erase_Entire_Sector(uint32_t addrx)
{
    #if USE_STM32F40x_FLASH_SET
    FLASH_EraseInitTypeDef FlashEraseInit;
    uint32_t SectorError = 0;
    FlashEraseInit.TypeErase = FLASH_TYPEERASE_SECTORS;     // 擦除类型，扇区擦除
    FlashEraseInit.Sector = STMFLASH_GetFlashSector(addrx); // 要擦除的扇区
    FlashEraseInit.NbSectors = 1;                           // 一次只擦除一个扇区
    FlashEraseInit.VoltageRange = FLASH_VOLTAGE_RANGE_3;    // 电压范围，VCC=2.7~3.6V之间!!
    HAL_FLASH_Unlock();     // 解锁
    uint8_t ret = HAL_FLASHEx_Erase(&FlashEraseInit, &SectorError);
    if ( ret != HAL_OK)
    {
        _DEBUG_PRINTF(DBG_ERR, "Error: Erase error code=[%d]\r\n", ret);
    }
    _DEBUG_PRINTF(DBG_STP, "Erased!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\r\n");
    FLASH_WaitForLastOperation(FLASH_WAITETIME); // 等待上次操作完成
    HAL_FLASH_Lock();       // 上锁
    #endif

    #if USE_STM32G0x0_FLASH_SET
    // uint32_t tmp_psize = 0U;

    // /* Check the parameters */
    // assert_param(IS_FLASH_SECTOR(Sector));
    // assert_param(IS_VOLTAGERANGE(VoltageRange));

    // if (VoltageRange == FLASH_VOLTAGE_RANGE_1)
    // {
    //     tmp_psize = FLASH_PSIZE_BYTE;
    // }
    // else if (VoltageRange == FLASH_VOLTAGE_RANGE_2)
    // {
    //     tmp_psize = FLASH_PSIZE_HALF_WORD;
    // }
    // else if (VoltageRange == FLASH_VOLTAGE_RANGE_3)
    // {
    //     tmp_psize = FLASH_PSIZE_WORD;
    // }
    // else
    // {
    //     tmp_psize = FLASH_PSIZE_DOUBLE_WORD;
    // }

    // /* Need to add offset of 4 when sector higher than FLASH_SECTOR_11 */
    // if (Sector > FLASH_SECTOR_11)
    // {
    //     Sector += 4U;
    // }
    // LL_FLASH_Erase_Page();
    return 1;
    #endif

    #if USE_STM32F1xx_FLASH_SET
    return 1;
    #endif
}

/**
 * @brief 读单个字
 * 
 * @param ReadAddr 读-头地址
 * @param pBuffer 传入数组
 * @return uint32_t 读出值
 */
uint32_t Msp_Flash_Read_Word(uint32_t ReadAddr)
{
    return *(uint32_t *)ReadAddr;
}

/**
 * @brief 读连续数组
 * 
 * @param ReadAddr 读-头地址
 * @param pBuffer 传入数组
 * @param number 写number个byte
 * @return uint32_t 读出“字”数
 */
uint32_t Msp_Flash_Read(uint32_t ReadAddr, uint32_t *pBuffer, uint32_t number)
{
    uint32_t i = 0;
    if (number > (FLASH_SECTOR_SIZE/sizeof(uint32_t)))
    {
        return 0;
    }
    for (i = 0; i < number; i++)
    {
        pBuffer[i++] = Msp_Flash_Read_Word(ReadAddr + (4 * i));
    }
    return i;
}

/**
 * @brief 写单个字
 * 
 * @param WriteAddr 写-头地址
 * @param pBuffer 传入数组
 * @return uint8_t 0-fail 1-success
 */
uint8_t Msp_Flash_Write_Word(uint32_t WriteAddr, uint32_t *pBuffer)
{

    return 0;
}

/**
 * @brief 写连续数组
 * 特别注意：因为STM32F4的扇区实在太大,没办法本地保存扇区数据,所以本函数
 * 写地址如果非0XFF,那么会先擦除整个扇区且不保存扇区数据.所以
 * 写非0XFF的地址,将导致整个扇区数据丢失.建议写之前确保扇区里
 * 没有重要数据,最好是整个扇区先擦除了,然后慢慢往后写.
 * 可参看该函数对OTP区域类似操作
 * OTP区域地址范围:0X1FFF7800~0X1FFF7A0F(注意：最后16字节，用于OTP数据块锁定，别乱写！！)
 * 
 * @param WriteAddr 写-头地址
 * @param pBuffer 传入数组
 * @param number 写number个word
 * @return uint32_t 
 */
uint32_t Msp_Flash_Write(uint32_t WriteAddr, uint32_t *pBuffer, uint32_t number)
{
    if (number > (FLASH_SECTOR_SIZE/sizeof(uint32_t)))
    {
        return 0;
    }

    #if USE_STM32F40x_FLASH_SET
    FLASH_EraseInitTypeDef FlashEraseInit;
    HAL_StatusTypeDef FlashStatus = HAL_OK;
    uint32_t SectorError = 0;
    uint32_t addrx = 0;
    uint32_t endaddr = 0;

    if (WriteAddr < STM32_FLASH_BASE || WriteAddr % 4)
        return 0; // 非法地址

    HAL_FLASH_Unlock();                     // 解锁
    addrx = WriteAddr;                      // 写入的起始地址
    endaddr = WriteAddr + number * 4;       // 写入的结束地址

    if (addrx < 0X1FFF0000)
    {
        while (addrx < endaddr) // 扫清一切障碍.(对非FFFFFFFF的地方,先擦除)
        {
            if (STMFLASH_ReadWord(addrx) != 0XFFFFFFFF) // 有非0XFFFFFFFF的地方,要擦除这个扇区
            {
                FlashEraseInit.TypeErase = FLASH_TYPEERASE_SECTORS;     // 擦除类型，扇区擦除
                FlashEraseInit.Sector = STMFLASH_GetFlashSector(addrx); // 要擦除的扇区
                FlashEraseInit.NbSectors = 1;                           // 一次只擦除一个扇区
                FlashEraseInit.VoltageRange = FLASH_VOLTAGE_RANGE_3;    // 电压范围，VCC=2.7~3.6V之间!!
                uint8_t ret = HAL_FLASHEx_Erase(&FlashEraseInit, &SectorError);
                uint8_t ret = HAL_ERROR;
                if ( ret != HAL_OK)
                {
                    // _DEBUG_PRINTF(DBG_ERR, "Error: Erase error code=[%d]\r\n", ret);
                    break; // 发生错误了
                }
            }
            else
                addrx += 4;
            FLASH_WaitForLastOperation(FLASH_WAITETIME); // 等待上次操作完成
        }
    }
    FlashStatus = FLASH_WaitForLastOperation(FLASH_WAITETIME); // 等待上次操作完成
    if (FlashStatus == HAL_OK)
    {
        while (WriteAddr < endaddr) // 写数据
        {
            if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_FAST, WriteAddr, *pBuffer) != HAL_OK) // 写入数据
            {
                break; // 写入异常
            }
            WriteAddr += 4;
            pBuffer++;
        }
    }
    HAL_FLASH_Lock(); // 上锁
    #endif

    #if USE_STM32G0x0_FLASH_SET
    uint32_t sta_addr = WriteAddr;                                                  // 开始地址
    uint32_t end_addr = WriteAddr + (number * sizeof(uint32_t));                    // 结束地址
    if ((WriteAddr < STM32_FLASH_BASE) || (WriteAddr % 4))                          // 非法地址
        return 0;
    if (Msp_Flash_Get_Sector(sta_addr) != Msp_Flash_Get_Sector(end_addr))           // 原则上不允许跨扇区读写
        return 0;
    LL_FLASH_Unlock();                                                              // 解锁
    while (sta_addr < end_addr)                                                     // 检查是否写过
    {
        if (Msp_Flash_Read_Word(sta_addr) != 0xFFFFFFFF)                            // 有非0XFFFFFFFF的地方,要擦除这个扇区
        {
            uint8_t ret = Msp_Flash_Erase_Entire_Sector(sta_addr);
            if (!Msp_Flash_Erase_Entire_Sector(sta_addr))
            {
                _DEBUG_PRINT(DBG_ERR, "Error: Erase error\r\n");
                LL_FLASH_Lock();                                                    // 上锁
                return 0;                                                           // 发生错误了
            }
        }
        else
            sta_addr += sizeof(uint32_t);
        while (LL_FLASH_IsActiveFlag_EOP() == 0);                                   // 等待上次操作完成
    }
    while (LL_FLASH_IsActiveFlag_EOP() == 0);                                       // 等待上次操作完成
    sta_addr = WriteAddr;                                                           // 重新赋值
    while (sta_addr < end_addr) // 写数据
    {
        // if (LL_FLASH_Program(FLASH_TYPEPROGRAM_FAST, sta_addr, *pBuffer) != HAL_OK)// 写入数据
        // {
        //     break; // 写入异常
        // }
        sta_addr += sizeof(uint32_t);
        pBuffer++;
    }
    LL_FLASH_Lock();                                                                // 上锁
    return 1;
    #endif

    #if USE_STM32F1xx_FLASH_SET
    return 1;
    #endif
}
